package top.dyzmj.detty.core.utils.internal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.dyzmj.detty.core.utils.SystemPropertyUtil;

import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 描述:
 * 与当前线程隔离的随机数生成器。
 * 与Math类使用的全局Random生成器一样，ThreadLocalRandom是用一个内部生成的种子进行初始化的，该种子不能被修改。
 * 如果可行，在并发程序中使用ThreadLocalRandom而不是shared Random对象通常会遇到更少的开销和争用。
 * 当多个任务(例如，每个io.net .util.internal.chmv8. forkjointask)在线程池中并行使用随机数时，使用ThreadLocalRandom尤其合适。
 * 该类的用法通常应该是:ThreadLocalRandom.current(). nextx(…)(其中X是Int、Long等)。
 * 当所有的用法都是这种形式时，就不可能在多个线程之间意外地共享ThreadLocalRandom。
 * 该类还提供了其他常用的有界随机生成方法
 *
 * @author dongYu
 * @date 2021/11/24
 */
public class ThreadLocalRandom extends Random {

	private static final Logger logger = LoggerFactory.getLogger(ThreadLocalRandom.class);

	private static final AtomicLong seedUniquifier = new AtomicLong();
	private static final Thread seedGeneratorThread;
	private static final BlockingQueue<Long> seedQueue;
	private static final long seedGeneratorStartTime;
	private static final long multiplier = 0x5DEECE66DL;
	private static final long addend = 0xBL;
	private static final long mask = (1L << 48) - 1;
	private static final long serialVersionUID = -5851777807851030925L;
	private static volatile long initialSeedUniquifier;
	private static volatile long seedGeneratorEndTime;

	static {
		initialSeedUniquifier = SystemPropertyUtil.getLong("top.dyzmj.initialSeedUniquifier", 0);
		if (initialSeedUniquifier == 0) {
			boolean secureRandom = SystemPropertyUtil.getBoolean("java.util.secureRandomSeed", false);
			if (secureRandom) {
				seedQueue = new LinkedBlockingQueue<Long>();
				seedGeneratorStartTime = System.nanoTime();

				// Try to generate a real random number from /dev/random.
				// Get from a different thread to avoid blocking indefinitely on a machine without much entropy.
				seedGeneratorThread = new Thread("initialSeedUniquifierGenerator") {
					@Override
					public void run() {
						final SecureRandom random = new SecureRandom(); // Get the real random seed from /dev/random
						final byte[] seed = random.generateSeed(8);
						seedGeneratorEndTime = System.nanoTime();
						long s = ((long) seed[0] & 0xff) << 56 |
								((long) seed[1] & 0xff) << 48 |
								((long) seed[2] & 0xff) << 40 |
								((long) seed[3] & 0xff) << 32 |
								((long) seed[4] & 0xff) << 24 |
								((long) seed[5] & 0xff) << 16 |
								((long) seed[6] & 0xff) << 8 |
								(long) seed[7] & 0xff;
						seedQueue.add(s);
					}
				};
				seedGeneratorThread.setDaemon(true);
				seedGeneratorThread.setUncaughtExceptionHandler((t, e) -> logger.debug("An exception has been raised by {}", t.getName(), e));
				seedGeneratorThread.start();
			} else {
				initialSeedUniquifier = mix64(System.currentTimeMillis()) ^ mix64(System.nanoTime());
				seedGeneratorThread = null;
				seedQueue = null;
				seedGeneratorStartTime = 0L;
			}
		} else {
			seedGeneratorThread = null;
			seedQueue = null;
			seedGeneratorStartTime = 0L;
		}
	}

	/**
	 * 初始化标志，允许对setSeed的调用只在执行Random构造函数时成功。
	 * 我们不能允许其他的，因为这会导致在程序的某个部分设置种子，从而无意中影响线程的其他用法。
	 */
	boolean initialized;
	/**
	 * 随机种子
	 */
	private long rnd;

	/**
	 * 构造函数只被localRandom.initialValue调用。
	 */
	ThreadLocalRandom() {
		super(newSeed());
		initialized = true;
	}

	public static long getInitialSeedUniquifier() {
		// Use the value set via the setter.
		long initialSeedUniquifier = ThreadLocalRandom.initialSeedUniquifier;
		if (initialSeedUniquifier != 0) {
			return initialSeedUniquifier;
		}

		synchronized (ThreadLocalRandom.class) {
			initialSeedUniquifier = ThreadLocalRandom.initialSeedUniquifier;
			if (initialSeedUniquifier != 0) {
				return initialSeedUniquifier;
			}

			// Get the random seed from the generator thread with timeout.
			final long timeoutSeconds = 3;
			final long deadLine = seedGeneratorStartTime + TimeUnit.SECONDS.toNanos(timeoutSeconds);
			boolean interrupted = false;
			for (; ; ) {
				final long waitTime = deadLine - System.nanoTime();
				try {
					final Long seed;
					if (waitTime <= 0) {
						seed = seedQueue.poll();
					} else {
						seed = seedQueue.poll(waitTime, TimeUnit.NANOSECONDS);
					}

					if (seed != null) {
						initialSeedUniquifier = seed;
						break;
					}
				} catch (InterruptedException e) {
					interrupted = true;
					logger.warn("Failed to generate a seed from SecureRandom due to an InterruptedException.");
					break;
				}

				if (waitTime <= 0) {
					seedGeneratorThread.interrupt();
					logger.warn(
							"Failed to generate a seed from SecureRandom within {} seconds. " +
									"Not enough entropy?", timeoutSeconds
					);
					break;
				}
			}

			// Just in case the initialSeedUniquifier is zero or some other constant
			initialSeedUniquifier ^= 0x3255ecdc33bae119L; // just a meaningless random number
			initialSeedUniquifier ^= Long.reverse(System.nanoTime());

			ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier;

			if (interrupted) {
				// Restore the interrupt status because we don't know how to/don't need to handle it here.
				Thread.currentThread().interrupt();

				// Interrupt the generator thread if it's still running,
				// in the hope that the SecureRandom provider raises an exception on interruption.
				seedGeneratorThread.interrupt();
			}

			if (seedGeneratorEndTime == 0) {
				seedGeneratorEndTime = System.nanoTime();
			}

			return initialSeedUniquifier;
		}
	}

	public static void setInitialSeedUniquifier(long initialSeedUniquifier) {
		ThreadLocalRandom.initialSeedUniquifier = initialSeedUniquifier;
	}

	private static long newSeed() {
		for (; ; ) {
			final long current = seedUniquifier.get();
			final long actualCurrent = current != 0 ? current : getInitialSeedUniquifier();

			// L'Ecuyer, "Tables of Linear Congruential Generators of Different Sizes and Good Lattice Structure", 1999
			final long next = actualCurrent * 181783497276652981L;

			if (seedUniquifier.compareAndSet(current, next)) {
				if (current == 0 && logger.isDebugEnabled()) {
					if (seedGeneratorEndTime != 0) {
						logger.debug(String.format(
								"-Dio.netty.initialSeedUniquifier: 0x%016x (took %d ms)",
								actualCurrent,
								TimeUnit.NANOSECONDS.toMillis(seedGeneratorEndTime - seedGeneratorStartTime)));
					} else {
						logger.debug(String.format("-Dio.netty.initialSeedUniquifier: 0x%016x", actualCurrent));
					}
				}
				return next ^ System.nanoTime();
			}
		}
	}

//	public static ThreadLocalRandom current() {
//		return InternalThreadLocalMap.get().random();
//	}

	// Borrowed from
	// http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/main/java/util/concurrent/ThreadLocalRandom.java
	private static long mix64(long z) {
		z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
		z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
		return z ^ (z >>> 33);
	}

	@Override
	public void setSeed(long seed) {
		if (initialized) {
			throw new UnsupportedOperationException();
		}
		rnd = (seed ^ multiplier) & mask;
	}

	@Override
	protected int next(int bits) {
		rnd = (rnd * multiplier + addend) & mask;
		return (int) (rnd >>> (48 - bits));
	}

	/**
	 * 在给定的最小值(包含值)和边界(排除值)之间返回一个伪随机的、均匀分布的值
	 *
	 * @param least 返回的最小值
	 * @param bound 上界（互斥）
	 * @return 下一个值
	 */
	public int nextInt(int least, int bound) {
		if (least >= bound) {
			throw new IllegalArgumentException();
		}
		return nextInt(bound - least) + least;
	}

	public long nextLong(long n) {
		if (n <= 0) {
			throw new IllegalArgumentException(n + " : " + n + " (expected: > 0)");
		}

		// Divide n by two until small enough for nextInt. On each
		// iteration (at most 31 of them but usually much less),
		// randomly choose both whether to include high bit in result
		// (offset) and whether to continue with the lower vs upper
		// half (which makes a difference only if odd).
		long offset = 0;
		while (n >= Integer.MAX_VALUE) {
			int bits = next(2);
			long half = n >>> 1;
			long nextn = ((bits & 2) == 0) ? half : n - half;
			if ((bits & 1) == 0) {
				offset += n - nextn;
			}
			n = nextn;
		}
		return offset + nextInt((int) n);
	}

	/**
	 * 在给定的最小值(包含值)和边界(排除值)之间返回一个伪随机的、均匀分布的值。
	 *
	 * @param least 返回的最小值
	 * @param bound 上界（互斥）
	 * @return 下一个值
	 */
	public long nextLong(long least, long bound) {
		if (least >= bound) {
			throw new IllegalArgumentException();
		}
		return nextLong(bound - least) + least;
	}

	/**
	 * 返回一个在0(包括)和指定值(不包括)之间的伪随机、均匀分布的双值。
	 *
	 * @param n 要返回的随机数的边界。必须是积极的。
	 * @return 下一个值
	 */
	public double nextDouble(double n) {
		if (n <= 0) {
			throw new IllegalArgumentException(n + " : " + n + " (expected: > 0)");
		}
		return nextDouble() * n;
	}

	/**
	 * 在给定的最小值(包含值)和边界(排除值)之间返回一个伪随机的、均匀分布的值。
	 *
	 * @param least 返回的最小值
	 * @param bound 上界（互斥）
	 * @return 下一个值
	 */
	public double nextDouble(double least, double bound) {
		if (least >= bound) {
			throw new IllegalArgumentException();
		}
		return nextDouble() * (bound - least) + least;
	}

}
